#include "LecteurPhraseAvecArbre.h"
#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
ls(nomFich), ts() {
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::analyse() {
    try {
        nbErreurs = 0;
        arbre=programme();
        cout << "Syntaxe correcte." << endl;
    }    catch (int nb) {
        cout << "Syntaxe incorrecte: " << nb << " erreurs" << endl;
        exit(0);
    }

}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::programme() {
    // <programme> ::= debut <seq_inst> fin FIN_FICHIER

    sauterSymCour("debut");
    Noeud* si = seqInst();
    while (ls.getSymCour() != "fin" && ls.getSymCour() != "<FINDEFICHIER>") {
        si = seqInst();
    }
    sauterSymCour("fin");
    try {
        testerSymCour("<FINDEFICHIER>");
    } catch (string ch) {
        cout << endl << "-------- Erreur ligne " << ls.getLigne()
                << " - Colonne " << ls.getColonne() << endl << "   Attendu : "
                << ch << endl << "   Trouve  : " << ls.getSymCour() << endl
                << endl;
    }
    try {
        if (nbErreurs > 0) {
            throw nbErreurs;
        }
    }    catch (int nb){
        throw nb;
    }
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::seqInst() {
    // <seqInst> ::= <inst> ; { <inst> ; }

    NoeudSeqInst* si = new NoeudSeqInst();
    do {
        if (ls.getSymCour() == "pour") {
            si->ajouteInstruction(inst());
        } else {
            si->ajouteInstruction(inst());
            sauterSymCour(";");
        }
    } while (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "repeter"
            || ls.getSymCour() == "tantque" || ls.getSymCour() == "si"
            || ls.getSymCour() == "lire" || ls.getSymCour() == "ecrire"
            || ls.getSymCour() == "pour");
    // tant que le symbole courant est un debut possible d'instruction...
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::inst() {
    // <inst> ::= <affectation> | <inst_condi>

    if (ls.getSymCour() == "si") {
        return instSi();
    } else if (ls.getSymCour() == "tantque") {
        return instTq();
    } else if (ls.getSymCour() == "repeter") {
        return instRepeter();
    } else if (ls.getSymCour() == "lire") {
        return instlire();
    } else if (ls.getSymCour() == "ecrire") {
        return instecrire();
    } else if (ls.getSymCour() == "pour") {
        return instPour();
     } else if (ls.getSymCour() == "<VARIABLE>") {
        return affectation();
    } else {
        erreur("<inst>");
        return NULL;
    }
}
///////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::affectation() {
    // <affectation> ::= <variable> = <expression>
    try {
        testerSymCour("<VARIABLE>");
    } catch (string ch) {
        cout << endl << "-------- Erreur ligne " << ls.getLigne()
                << " - Colonne " << ls.getColonne() << endl << "   Attendu : "
                << ch << endl << "   Trouve  : " << ls.getSymCour() << endl
                << endl;
    }
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour("=");
    Noeud* exp = expression();
    return new NoeudAffectation(var, exp);
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expression() {
    // <expression> ::= <facteur> { <opBinaire> <facteur> }

    Noeud* t = terme();
    while (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        Symbole operateurAdd = opAdd(); // on stocke le symbole de l'opérateur
        Noeud* termeDroit = terme(); // lecture de l'operande droit
        t = new NoeudOperateurBinaire(operateurAdd, t, termeDroit); // const. du noeud
    }
    return t;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expBool() {
    // <expBool> ::= <relation> { <opOu> <relation> }
    Noeud* terB = termeBool();
    while (ls.getSymCour() == "ou") {
        Symbole operateurOu = opOu();
        Noeud* terBDroit = termeBool();
        terB = new NoeudOperateurBinaire(operateurOu, terB, terBDroit);
    }
    return terB;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::facteur() {
    // <facteur> ::= <entier>  |  <variable>  |  - <facteur>  |  ( <expression> )
    Noeud* fact = NULL;
    if (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "<ENTIER>") {
        fact = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
        Symbole symCour = ls.getSymCour();
        ls.suivant();
        // on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
        fact = new NoeudOperateurBinaire(symCour, ts.chercheAjoute(Symbole("0")), facteur());
        fact = expBool();
    } else if (ls.getSymCour() == "(") {
        ls.suivant();
        fact = expBool();
        sauterSymCour(")");
    } else
        erreur("<facteur>");
    return fact;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::relation() {

    Noeud* rel = expression();
    while (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == "<" || ls.getSymCour() == ">" || ls.getSymCour() == "<=" || ls.getSymCour() == ">="
            || ls.getSymCour() == "et") {
        Symbole symCour;
        if (ls.getSymCour() == "et") {
            symCour = opEt();
            rel = new NoeudOperateurBinaire(symCour, rel, expBool());
        } else {
            symCour = opRel();
            rel = new NoeudOperateurBinaire(symCour, rel, expression());
        }
    }
    return rel;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::terme() //<terme> ::= <facteur> {<opAdd> <terme>}
{
    Noeud* ter = facteur();
    while (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        Symbole symCour = opMult();
        Noeud* fact = facteur();
        ter = new NoeudOperateurBinaire(symCour, ter, fact);
    }
    return ter;

}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::termeBool() {// <facteur>{<opEt><relation>}
    Noeud* terB = relation();
    while (ls.getSymCour() == "et") {
        Symbole symCour = opEt();
        terB = new NoeudOperateurBinaire(symCour, terB, relation());
    }
    return terB;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSi() {
    Noeud* si = NULL;
    if (ls.getSymCour() == "si") {
        Noeud* cond = NULL;
        Noeud* seq = NULL;
        NoeudInstSi* sinonsi = NULL;
        Noeud* seqSinon = NULL;
        ls.suivant();
        sauterSymCour("(");
        cond = expBool(); //Affectation de la condition
        sauterSymCour(")");
        seq = seqInst(); //Affectation de la sequence d'intructions

        int i = 0;
        while (ls.getSymCour() == "sinonsi") {
            Noeud* CondSinonSi = NULL;
            Noeud* seqSinonSi = NULL;
            ls.suivant();
            sauterSymCour("(");
            CondSinonSi = expBool(); //Affectation de la condition de chaque sinonsi
            sauterSymCour(")");
            seqSinonSi = seqInst(); //Affectation de la sequence d'intructions sinonsi
            if (i == 0) {
                sinonsi = new NoeudInstSi(CondSinonSi, seqSinonSi, NULL, NULL,true);
            } else {
                sinonsi->ajouteQueueSinonsi(CondSinonSi, seqSinonSi);
            }
            i = i + 1;
        }

        if (ls.getSymCour() == "sinon") {
            ls.suivant();
            seqSinon = seqInst();
        }
        sauterSymCour("finsi");
        si = new NoeudInstSi(cond, seq, sinonsi, seqSinon); //Création du noeud tq
    } else erreur("<instSi>");
    return si;
}

Noeud* LecteurPhraseAvecArbre::instTq() {
    Noeud* tq = NULL;
    if (ls.getSymCour() == "tantque") {
        Noeud* cond = NULL;
        Noeud* seq = NULL;
        ls.suivant();
        sauterSymCour("(");
        cond = expBool(); //Création d'une expression Bool cond
        sauterSymCour(")");
        seq = seqInst(); //Création d'une seqInst seq
        sauterSymCour("fintantque");
        tq = new NoeudInstTq(cond, seq); //Création du noeud tq
    } else erreur("<instTq>");

    return tq;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instPour() {// pour ( <affectation>;<expBool>;<affectation> ){<seqInst>}
    Noeud* pour = NULL;
    if (ls.getSymCour() == "pour") {
        ls.suivant();
        sauterSymCour("(");
        Noeud* affInit = affectation();
        sauterSymCour(";");
        Noeud* cond = expBool();
        sauterSymCour(";");
        Noeud* affIter = affectation();
        sauterSymCour(")");
        sauterSymCour("{");
        Noeud* seq = seqInst();
        sauterSymCour("}");
        pour = new NoeudPour(cond, seq, affInit, affIter); //Création du noeud tq
    } else {
        erreur("<instPour>");
    }

    return pour;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instRepeter() {
    Noeud* rep = NULL;
    if (ls.getSymCour() == "repeter") {
        ls.suivant();
        Noeud* seq = seqInst();
        if (ls.getSymCour() == "jusqua") {
            ls.suivant();
            sauterSymCour("(");
            Noeud* cond = expBool();
            sauterSymCour(")");
            rep = new NoeudRepeter(cond, seq); //Création du noeud tq 
        }
    }
    return rep;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instlire() {
    Noeud* lire = NULL;
    if (ls.getSymCour() == "lire") {
        ls.suivant();
        sauterSymCour("(");
        try {
            testerSymCour("<VARIABLE>");
        }        catch (string ch) {
            cout << endl << "-------- Erreur ligne " << ls.getLigne()
                    << " - Colonne " << ls.getColonne() << endl << "   Attendu : "
                    << ch << endl << "   Trouve  : " << ls.getSymCour() << endl
                    << endl;
        }
        Noeud* variable = ts.chercheAjoute(ls.getSymCour());
        lire = new NoeudInstLire(variable);
        ls.suivant();
        sauterSymCour(")");
    } else erreur("<instlire>");
    return lire;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instecrire() {
    Noeud* ecrire = NULL;
    if (ls.getSymCour() == "ecrire") {
        ls.suivant();
        sauterSymCour("(");
        if (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "<CHAINE>") {
            Noeud* variable = ts.chercheAjoute(ls.getSymCour());
            ecrire = new NoeudInstEcrire(variable);
            ls.suivant();
        } else {
            ecrire = new NoeudInstEcrire(expression());
        }
        sauterSymCour(")");
    } else
        erreur("<instecrire>");
    return ecrire;
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opBinaire() {
    // <opBinaire> ::= + | - | *  | /
    Symbole operateur;
    if (ls.getSymCour() == "+" || ls.getSymCour() == "-" ||
            ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opBinaire>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opAdd() { //  <opAdd> ::= +|- /
    Symbole sym;
    if (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        sym = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opAdd>");
    return sym;
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult() { //  <opAdd> ::= +|- /
    Symbole sym;
    if (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        sym = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opMult>");
    return sym;
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opEt() {//  <opBool> ::= et|ou /
    Symbole sym;
    if (ls.getSymCour() == "et") {
        sym = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opEt>");
    return sym;
}

Symbole LecteurPhraseAvecArbre::opOu() {//  <opBool> ::= et|ou /
    Symbole sym;
    if (ls.getSymCour() == "ou") {
        sym = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opOu>");
    return sym;
}



////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opRel() {
    //  <opBool> ::= ==|!=|>|<=|>=|< /

    Symbole sym;
    if (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == "<" || ls.getSymCour() == ">" || ls.getSymCour() == "<=" || ls.getSymCour() == ">=") {
        sym = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opRel>");
    return sym;

}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opUnaire() {
    //  <opUnaire> ::= -|non
    Symbole sym;
    if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
        Symbole sym = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opUnaire>");

    return sym;
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::testerSymCour(string ch) {
    try {
        if (ls.getSymCour() != ch) {
            throw ch; //  on leve une exception
        }
    } catch (string symb) {
        nbErreurs = nbErreurs + 1;
        throw symb;
    }
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
    try {
        testerSymCour(ch);
        ls.suivant();
    } catch (string symb) {
        cout << endl << "-------- Erreur ligne " << ls.getLigne()
                << " - Colonne " << ls.getColonne() << endl << "   Attendu : "
                << ch << endl << "   Trouve  : " << ls.getSymCour() << endl
                << endl;
    }
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::erreur(string mess) {

    try {
        throw mess;
    } catch (string message) {
        nbErreurs = nbErreurs + 1;
        cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
                << ls.getColonne() << endl << "   Attendu : " << message << endl
                << "   Trouve  : " << ls.getSymCour() << endl << endl;
        ls.suivant();
    } // plus tard, on levera une exception
}
